home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / OWLSRC.PAK / COLMNHDR.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  30.7 KB  |  1,173 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // Copyright (c) 1995, 1997 by Borland International, All Rights Reserved
  4. //
  5. //$Revision:   10.11  $
  6. //
  7. //----------------------------------------------------------------------------
  8. #include <owl/pch.h>
  9.  
  10. #if !defined(OWL_COLMNHDR_H)
  11. #include <owl/colmnhdr.h>
  12. #endif
  13.  
  14. #if !defined(OWL_NATIVECTRL_ALWAYS)
  15. # if !defined(OWL_UIHELPER_H)
  16. #   include <owl/uihelper.h>
  17. # endif
  18. # if !defined(OWL_COLMNHDR_RH)
  19. #   include <owl/colmnhdr.rh>
  20. # endif
  21. #endif
  22.  
  23. OWL_DIAGINFO;
  24. DIAG_DECLARE_GROUP(OwlCommCtrl);
  25.  
  26. //
  27. // Default size of string and bitmap items, both measured in pixels
  28. //
  29. int  THdrItem::DefStringItemSize = 100;
  30. int  THdrItem::DefBitmapItemSize = 32;
  31.  
  32. //
  33. // API to allow user to update ObjectWindows' default string size.
  34. // NOTE: Since the variable maintaining that information is shared
  35. //       by every instance of this class, use this function with
  36. //       caution.
  37. //
  38. void
  39. THdrItem::SetDefStringSize(int size)
  40. {
  41.   DefStringItemSize = size;
  42. }
  43.  
  44. //
  45. // API to allow the user to update ObjectWindows' default bitmap
  46. // item size.
  47. // NOTE: Since the variable maintaining that information is shared
  48. //       by every instance of this class, use this function with
  49. //       caution.
  50. void
  51. THdrItem::SetDefBitmapSize(int size)
  52. {
  53.   DefBitmapItemSize = size;
  54. }
  55.  
  56. //
  57. // Constructs a THdrItem object for an item consisting of
  58. // both a string and a bitmapped image.
  59. //
  60. THdrItem::THdrItem(const char far* str, HBITMAP hbm)
  61. {
  62.   mask = HDI_FORMAT;
  63.   fmt  = HDF_LEFT;
  64.   SetText(str);
  65.   SetWidth(DefStringItemSize);
  66.   SetBitmap(hbm);
  67.   lParam = 0;
  68. }
  69.  
  70. //
  71. // Constructs a THdrItem object for an item which has a string
  72. // Defaults to left alignment and the DefaultStringItemSize.
  73. //
  74. THdrItem::THdrItem(const char far* str)
  75. {
  76.   mask = HDI_FORMAT;
  77.   fmt  = HDF_LEFT;
  78.   SetText(str);
  79.   SetWidth(DefStringItemSize);
  80.   lParam = 0;
  81. }
  82.  
  83. //
  84. // Constructs a THdrItem object for an item which has a bitmapped image
  85. // Defaults to left alignment and the default bitmap item size.
  86. //
  87. THdrItem::THdrItem(HBITMAP hbm)
  88. {
  89.   mask = HDI_FORMAT;
  90.   fmt  = HDF_LEFT;
  91.   SetBitmap(hbm);
  92.   SetWidth(DefBitmapItemSize);
  93.   lParam = 0;
  94. }
  95.  
  96. //
  97. // Constructs an 'empty' THdrItem with the specified 'msk' enabled.
  98. // This flavour of the constructor is mainly used to construct an object
  99. // which is used to retrieve information about an existing item.
  100. //
  101. THdrItem::THdrItem(uint msk)
  102. {
  103.   mask = msk;
  104. }
  105.  
  106. //
  107. // Constructs an 'empty' THdrItem with the specified 'msk' enabled.
  108. // This flavour of the constructor is mainly used to construct an object
  109. // which is used to retrieve information about an existing item. 'buffer'
  110. // specifies a location which will receive the text of the item and 'len'
  111. // specifies the size of the buffer.
  112. //
  113. THdrItem::THdrItem(char far* buffer, int len, uint msk)
  114. {
  115.   mask = msk;
  116.   cchTextMax = len;
  117.   pszText = buffer;
  118.   hbm = 0;
  119.   lParam = 0;
  120. }
  121.  
  122. //
  123. // Sets the text of the HeaderItem object.
  124. // NOTE: The format flags is not updated to contain any alignment flags
  125. //
  126. void
  127. THdrItem::SetText(const char far* str)
  128. {
  129.   mask      |= (HDI_TEXT|HDI_FORMAT);
  130.   fmt       |= (HDF_STRING);
  131.   pszText    = CONST_CAST(char far*, str);
  132.   cchTextMax = strlen(str);     // This is probably superfluous,
  133.                                 // albeit harmless
  134. }
  135.  
  136. //
  137. // Sets the bitmap handle of the HeaderItem object.
  138. // NOTE: The format flags is not updated to contain any alignment flags
  139. //
  140. void
  141. THdrItem::SetBitmap(HBITMAP bitmap)
  142. {
  143.   mask |= (HDI_BITMAP|HDI_FORMAT);
  144.   fmt  |= HDF_BITMAP;
  145.   hbm   = bitmap;
  146. }
  147.  
  148. //
  149. // Sets the width of the item
  150. //
  151. void
  152. THdrItem::SetWidth(int width)
  153. {
  154.   cxy = width;
  155.   mask &= ~HDI_HEIGHT;
  156.   mask |=  HDI_WIDTH;
  157. }
  158.  
  159. //
  160. // Sets the height of the item
  161. //
  162. void
  163. THdrItem::SetHeight(int height)
  164. {
  165.   cxy = height;
  166.   mask &= ~HDI_WIDTH;
  167.   mask |=  HDI_HEIGHT;
  168. }
  169.  
  170. //
  171. // Sets the application-defined item data attached to this item object
  172. //
  173. void
  174. THdrItem::SetItemData(TParam2 data)
  175. {
  176.   lParam = data;
  177.   mask  |= HDI_LPARAM;
  178. }
  179.  
  180.  
  181. //----------------------------------------------------------------------------
  182.  
  183. //
  184. // constructor for TColumnHeader
  185. //
  186. // initializes its data fields using parameters passed and default values
  187. //
  188. // by default, a ColumnHeader associated with the TColumnHeader will:
  189. //   - be visible upon creation
  190. //   - have a border, divider tracks
  191. //   - be a horizontal header window
  192. //
  193. TColumnHeader::TColumnHeader(TWindow*   parent,
  194.                              int        id,
  195.                              int x, int y, int w, int h,
  196.                              TModule*   module)
  197. :
  198.   TControl(parent, id, 0, x, y, w, h, module)
  199. #if !defined(OWL_NATIVECTRL_ALWAYS)
  200.   , Item(1,0,3) , Font(NULL)
  201. #endif
  202. {
  203.   // By default we'll use the native implementation if it's available
  204.   //
  205.   NativeUse = TCommCtrl::IsAvailable () ? ClassNativeUse : nuNever;
  206.  
  207.   // here's a strange: although Win95 defines HDS_HORZ, it is defined as '0',
  208.   // which would indicate that the only column headings supported are horizontal
  209.   // headings.  Also, there is no HDS_VERT.
  210.   // In any case, we explicitly 'test' for HDS_HORZ in the code below and we
  211.   // try to forsee what could happen if (and when) HDS_VERT gets defined.
  212.   Attr.Style |= HDS_HORZ;
  213.  
  214. #if !defined(OWL_NATIVECTRL_ALWAYS)
  215.   // When running in an environment where the system does not provide
  216.   // Common Controls we need to initialize the variables used for
  217.   // emulating a column heading
  218.   //
  219.   if (NativeUse != nuAlways)
  220.     InitCtl();
  221. #else
  222.   // When OWL is built with the NATIVECTRL_ALWAYS option, the
  223.   // Common Control library MUST be available....
  224.   //
  225.   CHECK(TCommCtrl::IsAvailable());
  226. #endif
  227.  
  228.   TRACEX(OwlCommCtrl, OWL_CDLEVEL, "TColumnHeader constructed @" << (void*)this);
  229. }
  230.  
  231. //
  232. // Constructor of a ColumnHeader object which aliases a control found
  233. // within a dialog.
  234. //
  235. TColumnHeader::TColumnHeader(TWindow*   parent,
  236.                              int        resourceId,
  237.                              TModule*   module)
  238. :
  239.   TControl(parent, resourceId, module)
  240. #if !defined(OWL_NATIVECTRL_ALWAYS)
  241.   ,Item(1,0,3),Font(NULL)
  242. #endif
  243. {
  244.  
  245.   NativeUse = TCommCtrl::IsAvailable() ? ClassNativeUse : nuNever;
  246.  
  247. #if !defined(OWL_NATIVECTRL_ALWAYS)
  248.   // When running in an environment where the system does not provide
  249.   // Common Controls we need to initialize the variables used for
  250.   // emulating a tabcontrol...
  251.   //
  252.   if (NativeUse != nuAlways)
  253.     InitCtl();
  254. #else
  255.   // When OWL is built with the NATIVECTRL_ALWAYS option, the
  256.   // Common Control library MUST be available....
  257.   //
  258.   CHECK(TCommCtrl::IsAvailable());
  259. #endif
  260.   TRACEX(OwlCommCtrl, OWL_CDLEVEL, "TColumnHeader constructed from resource @" << (void*)this);
  261. }
  262.  
  263.  
  264. TColumnHeader::~TColumnHeader()
  265. {
  266. #if !defined(OWL_NATIVECTRL_ALWAYS)
  267.   // When running in an environment where the system does not provide
  268.   // Common Controls we need to cleanup the variables used for
  269.   // emulating a column control...
  270.   //
  271.   if (!(NativeUse & nuUsing)) {
  272.     CleanupCtl();
  273.   }
  274. #endif
  275. }
  276.  
  277. void TColumnHeader::InitCtl()
  278. {
  279.   Font = new TDefaultGUIFont();
  280.   Operation = NONE;
  281.   Pressed = -1;
  282. }
  283.  
  284.  
  285. void TColumnHeader::CleanupCtl()
  286. {
  287.   delete Font;
  288. }
  289.  
  290. //
  291. // Returns the class name of the underlying control associated with
  292. // the TColumnHeader object.
  293. // NOTE: The logic used depends on the availability of native
  294. //       Common Control support. In the case where OWL provides
  295. //       the underlying support, we'll specify a TColumnHeader -specific
  296. //       classname although that's not necessary [it eases debugging]
  297. //
  298. char far*
  299. TColumnHeader::GetClassName()
  300. {
  301. #if defined(OWL_NATIVECTRL_ALWAYS)
  302.   // When OWL is built with the NATIVECTRL_ALWAYS option, the
  303.   // Common Control library MUST be available....
  304.   //
  305.   PRECONDITION(TCommCtrl::IsAvailable());
  306.   NativeUse = TNativeUse(NativeUse | nuUsing);
  307.   return WC_HEADER;
  308. #else
  309.   // Pick name based on availability of native OS support
  310.   //
  311.   char far* name = (NativeUse & nuAlways) ? WC_HEADER : "OWL_ColumnHeader";
  312.  
  313.   // Update flags (??)
  314.   //
  315.   if (NativeUse & nuAlways)
  316.     NativeUse = TNativeUse(NativeUse | nuUsing);
  317.   else
  318.     NativeUse = TNativeUse(NativeUse & ~nuUsing);
  319.  
  320.   return name;
  321. #endif
  322. }
  323.  
  324. //
  325. // If successful this method returns the number of items in the
  326. // header control. In case of failure it returns -1.
  327. //
  328. int
  329. TColumnHeader::GetCount() const
  330. {
  331.   if (NativeUse & nuUsing) {
  332.     return (int)CONST_CAST(TColumnHeader*,this)->SendMessage(HDM_GETITEMCOUNT);
  333.   }
  334.   else {
  335.     return Item.GetItemsInContainer();
  336.   }
  337. }
  338.  
  339. //
  340. // This method retrieves the size and position of a header control
  341. // within a given rectangle. It determines the appropriate dimensions
  342. // of a new header control that is to occupy the given rectangle.
  343. // Upon entry the 'boundingRect' parameter specifies the rectangle
  344. // within which the columnHeader must lie. The control then updates
  345. // the WINDOWPOS structure to contain the desired/appropriate dimensions
  346. // for the control to occupy within the specified rectangle.
  347. //
  348. bool
  349. TColumnHeader::Layout(TRect& boundingRect, WINDOWPOS& wp)
  350. {
  351.   if(NativeUse & nuUsing) {
  352.     HD_LAYOUT hdl;
  353.     hdl.prc   = &boundingRect;
  354.     hdl.pwpos = ℘
  355.     return SendMessage(HDM_LAYOUT, 0, TParam2(&hdl)) != 0;
  356.   }
  357.   else {
  358.     wp.hwnd = GetHandle();
  359.     wp.hwndInsertAfter = NULL;
  360.     wp.flags = SWP_NOZORDER;
  361.     wp.x = boundingRect.left;
  362.     wp.y = boundingRect.top;
  363.  
  364.     TClientDC dc(GetHandle());
  365.  
  366.     if(Font!=NULL) {
  367.       dc.SelectObject(*Font);
  368.     }
  369.  
  370.     TEXTMETRIC tm;
  371.     dc.GetTextMetrics(tm);
  372.  
  373.     // The hard-coded value represents extra border space.  This could be
  374.     // computed using TUIBorder, but there is no easy way to extract the space
  375.     // used by the edges.
  376.     const int BORDER_SPACE=4;
  377.  
  378.     if((Attr.Style & HDS_HORZ)==HDS_HORZ) {
  379.       wp.cx=boundingRect.Width();
  380.       wp.cy=tm.tmHeight+BORDER_SPACE;
  381.     }
  382.     else {
  383.       wp.cy=boundingRect.Height();
  384.       wp.cx=tm.tmMaxCharWidth+BORDER_SPACE;
  385.     }
  386.     return true;
  387.   }
  388. }
  389.  
  390. //
  391. // This method repositions the columnHeader control within the client area
  392. // of its parent window by taking advantage of the columnHeader's ability
  393. // to specify its desired/appropriate position from a specified bounding
  394. // rectangle. This method assumes that the control will occupy the full
  395. // width of the client area of its parent.
  396. //
  397. bool
  398. TColumnHeader::Layout(uint swpFlags)
  399. {
  400.   WINDOWPOS wp;
  401.   TRect rect = Parent->GetClientRect();
  402.   if (Layout(rect, wp)) {
  403.     SetWindowPos(wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
  404.                  wp.flags|swpFlags);
  405.     return true;
  406.   }
  407.   return false;
  408. }
  409.  
  410. //
  411. // Adds a new item to the column header.
  412. // The following illustrates a typical use of the 'Add' method:
  413. //
  414. //      THdrItem hdrItem("&Employee Names");
  415. //      hdr.Add(hdrItem);
  416. //
  417. int
  418. TColumnHeader::Add(const THdrItem& item)
  419. {
  420.   return Insert(item, GetCount());
  421. }
  422.  
  423. //
  424. // Inserts a new item at the specified location, 'index', in the
  425. // columnHeader control.
  426. // The following illustrates a typical use of the 'Insert' method:
  427. //
  428. //    THdrItem hdrItem(GetModule().LoadBitmap(IDB_COMPANYLOGO));
  429. //    hdr.Insert(hdrItem, 0);
  430. //
  431. int
  432. TColumnHeader::Insert(const THdrItem& item, int index)
  433. {
  434.   if (NativeUse & nuUsing) {
  435.     return SendMessage(HDM_INSERTITEM, index, TParam2(&item)) != 0;
  436.   }
  437.   else {
  438.     if(index < 0) {
  439.       return false;
  440.     }
  441.     if((uint)index > Item.GetItemsInContainer()) {
  442.       index = Item.GetItemsInContainer();
  443.     }
  444.     TItem it(item);
  445.     Item.AddAt(item,index);
  446.     Invalidate();
  447.     return true;
  448.   }
  449. }
  450.  
  451. //
  452. // Deletes the item at the specified 'index' from the header control.
  453. //
  454. bool
  455. TColumnHeader::Delete(int index)
  456. {
  457.   if (NativeUse & nuUsing) {
  458.     return SendMessage(HDM_DELETEITEM, index) != FALSE;
  459.   }
  460.   else {
  461.     if(index<0 || (uint)index >= Item.GetItemsInContainer()) {
  462.       return false;
  463.     }
  464.     Item.Detach(index);
  465.     Invalidate();
  466.     return true;
  467.   }
  468. }
  469.  
  470. //
  471. // Retrieves information about the item at the specified index by filling
  472. // out the 'itemInfo' structure passed in. The 'msk' contains one or more
  473. // HDI_xxxx constants and can be used to specify which information should
  474. // be copied.
  475. //
  476. bool
  477. TColumnHeader::GetItem(THdrItem& itemInfo, int index, uint msk)
  478. {
  479.   itemInfo.mask |= msk;
  480.   if (NativeUse & nuUsing) {
  481.     return SendMessage(HDM_GETITEM, index, TParam2(&itemInfo)) != 0;
  482.   }
  483.   else {
  484.     if(index<0 || (uint)index>=Item.GetItemsInContainer()) {
  485.       return false;
  486.     }
  487.  
  488.     const TItem &it=Item[index];
  489.  
  490.     if(itemInfo.mask & it.mask & HDI_BITMAP) {
  491.       itemInfo.hbm=it.hbm;
  492.     }
  493.     if(itemInfo.mask & it.mask & HDI_FORMAT) {
  494.       itemInfo.fmt=it.fmt;
  495.     }
  496.     if(itemInfo.mask & it.mask & (HDI_HEIGHT|HDI_WIDTH)) {
  497.       itemInfo.cxy=it.cxy;
  498.     }
  499.     if(itemInfo.mask & it.mask & HDI_LPARAM) {
  500.       itemInfo.lParam=it.lParam;
  501.     }
  502.     if(itemInfo.mask & it.mask & HDI_TEXT) {
  503.       itemInfo.pszText=it.pszText;
  504.       itemInfo.cchTextMax=it.cchTextMax;
  505.     }
  506.     return true;
  507.   }
  508. }
  509.  
  510. //
  511. // Updates the attribute(s) a the item at the specified 'index'. The
  512. // 'itemInfo' structure contains the new attributes of the item.
  513. //
  514. bool
  515. TColumnHeader::SetItem(const THdrItem& itemInfo, int index)
  516. {
  517.   if (NativeUse & nuUsing) {
  518.     return SendMessage(HDM_SETITEM, index, TParam2(&itemInfo)) != 0;
  519.   }
  520.   else {
  521.     if(index<0 || (uint)index>=Item.GetItemsInContainer()) {
  522.       return false;
  523.     }
  524.  
  525.     if(SendNotify(HDN_ITEMCHANGING,index)) {
  526.       return false;
  527.     }
  528.  
  529.     TItem &it=Item[index];
  530.  
  531.     if(itemInfo.mask & HDI_BITMAP) {
  532.       it.hbm = itemInfo.hbm;
  533.       it.mask |= HDI_BITMAP;
  534.     }
  535.     if(itemInfo.mask & HDI_FORMAT) {
  536.       it.fmt = itemInfo.fmt;
  537.       it.mask |= HDI_FORMAT;
  538.     }
  539.     // note that HDI_WIDTH==HDI_HEIGHT, so only one check needed
  540.     if(itemInfo.mask & HDI_WIDTH) {
  541.       it.cxy = itemInfo.cxy;
  542.       it.mask |= HDI_WIDTH;
  543.     }
  544.     if(itemInfo.mask & HDI_LPARAM) {
  545.       it.lParam = itemInfo.lParam;
  546.       it.mask |= HDI_LPARAM;
  547.     }
  548.     if(itemInfo.mask & HDI_TEXT) {
  549.       if(it.mask & HDI_TEXT) {
  550.         delete [] it.pszText;
  551.       }
  552.       it.pszText = new char[itemInfo.cchTextMax];
  553.       memcpy(it.pszText, itemInfo.pszText, itemInfo.cchTextMax);
  554.       it.cchTextMax = itemInfo.cchTextMax;
  555.       it.mask |= HDI_TEXT;
  556.     }
  557.  
  558.     Invalidate();
  559.     SendNotify(HDN_ITEMCHANGED, index);
  560.  
  561.     return true;
  562.   }
  563. }
  564.  
  565. //
  566. // Transfers are not implemented for ColumnHeaders. Simply return 0.
  567. //
  568.  
  569. uint
  570. TColumnHeader::Transfer(void* /*buffer*/, TTransferDirection /*direction*/)
  571. {
  572.   TRACEX(OwlCommCtrl, OWL_CDLEVEL, "TColumnHeader::Transfer is not"\
  573.                                    "implemented!");
  574.   return 0;
  575. }
  576.  
  577. //
  578. // Tests a point to determine which header item, if any, is at the
  579. // specified point.
  580. // NOTE: The coordinates are specified via the 'pt' member of the
  581. //       THeaderHitTestInfo parameter.
  582. //
  583.  
  584. int
  585. TColumnHeader::HitTest(THeaderHitTestInfo& ht)
  586. {
  587.   if (NativeUse & nuUsing) {
  588.     return (int)SendMessage(HDM_HITTEST, 0, TParam2(&ht));
  589.   }
  590.   else {
  591.     // we need the client rectangle to check for hits...
  592.     TRect clientRect=GetClientRect();
  593.  
  594.     // if not inside client rectangle, return above or below
  595.     if(!clientRect.Contains(ht.pt)) {
  596.       ht.flags=0;
  597.  
  598.       if(ht.pt.x<clientRect.left) {
  599.         ht.flags|=HHT_TOLEFT;
  600.       }
  601.       else if(ht.pt.x>=clientRect.right) {
  602.         ht.flags|=HHT_TORIGHT;
  603.       }
  604.  
  605.       if(ht.pt.y<clientRect.top) {
  606.         ht.flags|=HHT_ABOVE;
  607.       }
  608.       else if(ht.pt.y>=clientRect.bottom) {
  609.         ht.flags|=HHT_BELOW;
  610.       }
  611.  
  612.       return ht.iItem=-1;
  613.     }
  614.  
  615.     // Determine hot box around dividing line
  616.     TRect hotBox = clientRect;
  617.     TSize hotSize(GetSystemMetrics(SM_CXDOUBLECLK), GetSystemMetrics(SM_CYDOUBLECLK));
  618.  
  619.     if((Attr.Style & HDS_HORZ)==HDS_HORZ) {
  620.       hotBox.left = -hotSize.cx;
  621.       hotBox.right = hotSize.cx;
  622.     }
  623.     else {
  624.       hotBox.top = -hotSize.cy;
  625.       hotBox.bottom = hotSize.cy;
  626.     }
  627.  
  628.     // Scan for the split position associated with the given point
  629.     for(uint index=0; index < Item.GetItemsInContainer(); ++index) {
  630.       // Move the hot box
  631.       if((Attr.Style & HDS_HORZ)==HDS_HORZ)
  632.         hotBox.Offset(Item[index].cxy,0);
  633.       else
  634.         hotBox.Offset(0,Item[index].cxy);
  635.  
  636.       // If a next item exists, define a hot box beyond its start edge. If this zone is hit,
  637.       // we will return the next item.
  638.       if(index < Item.GetItemsInContainer()-1) {
  639.         TRect nextHotBox = hotBox;
  640.  
  641.         if((Attr.Style & HDS_HORZ) == HDS_HORZ) {
  642.           nextHotBox.right = hotBox.right+Item[index+1].cxy;
  643.           nextHotBox.left = nextHotBox.right-hotSize.cx;
  644.         }
  645.         else {
  646.           nextHotBox.bottom = hotBox.bottom+Item[index+1].cxy;
  647.           nextHotBox.top = nextHotBox.bottom-hotSize.cy;
  648.         }
  649.         if(nextHotBox.Contains(ht.pt) && nextHotBox.Touches(hotBox)) {
  650.           ht.flags = HHT_ONDIVOPEN;
  651.           ht.iItem = index+1;
  652.           return ht.iItem;
  653.         }
  654.       }
  655.  
  656.       // Check if hot box contains point
  657.       if(hotBox.Contains(ht.pt)) {
  658.         ht.flags=HHT_ONDIVIDER;
  659.         ht.iItem=index;
  660.         return ht.iItem;
  661.       }
  662.  
  663.       // check if it is in the item itself
  664.       TRect itemRect;
  665.       if((Attr.Style & HDS_HORZ)==HDS_HORZ) {
  666.         itemRect.left=hotBox.left+hotSize.cx-Item[index].cxy;
  667.         itemRect.right=hotBox.left;
  668.         itemRect.top=hotBox.top;
  669.         itemRect.bottom=hotBox.bottom;
  670.       }
  671.       else {
  672.         itemRect.left=hotBox.left;
  673.         itemRect.right=hotBox.right;
  674.         itemRect.top=hotBox.top+hotSize.cy-Item[index].cxy;
  675.         itemRect.bottom=hotBox.top;
  676.       }
  677.  
  678.       if(itemRect.Contains(ht.pt)) {
  679.         ht.flags = HHT_ONHEADER;
  680.         ht.iItem = index;
  681.         return ht.iItem;
  682.       }
  683.     }
  684.     ht.flags = HHT_NOWHERE;
  685.     return ht.iItem = -1;
  686.   }
  687. }
  688.  
  689. #if !defined(OWL_NATIVECTRL_ALWAYS)
  690. DEFINE_RESPONSE_TABLE1(TColumnHeader, TControl)
  691.   EV_WM_GETFONT,
  692.   EV_WM_SETFONT,
  693.   EV_WM_LBUTTONDOWN,
  694.   EV_WM_MOUSEMOVE,
  695.   EV_WM_LBUTTONUP,
  696.   EV_WM_LBUTTONDBLCLK,
  697.   EV_WM_SETCURSOR,
  698. END_RESPONSE_TABLE;
  699.  
  700. TNativeUse TColumnHeader::ClassNativeUse=nuAlways;
  701.  
  702. TColumnHeader::TItem::TItem()
  703. {
  704.   mask=0;
  705. }
  706.  
  707. TColumnHeader::TItem::TItem(const HD_ITEM &other)
  708. {
  709.   Init(other);
  710. }
  711.  
  712. TColumnHeader::TItem::~TItem()
  713. {
  714.   Cleanup();
  715. }
  716.  
  717. TColumnHeader::TItem &TColumnHeader::TItem::operator=(const TItem &other)
  718. {
  719.   if(this!=&other) {
  720.     Cleanup();
  721.     Init(other);
  722.   }
  723.   return *this;
  724. }
  725.  
  726. bool TColumnHeader::TItem::operator==(const TItem &other) const
  727. {
  728.   return this==&other;
  729. }
  730.  
  731. void TColumnHeader::TItem::Cleanup()
  732. {
  733.   if(mask & HDI_TEXT) {
  734.     delete [] pszText;
  735.   }
  736.   mask=0;
  737. }
  738.  
  739. void TColumnHeader::TItem::Init(const HD_ITEM &other)
  740. {
  741.   mask = other.mask;
  742.   cxy = other.cxy;
  743.   hbm = other.hbm;
  744.   fmt = other.fmt;
  745.   lParam = other.lParam;
  746.   if(other.mask & HDI_TEXT) {
  747.     pszText=new char[cchTextMax=other.cchTextMax];
  748.     memcpy(pszText,other.pszText,other.cchTextMax);
  749.   }
  750. }
  751.  
  752. LRESULT TColumnHeader::SendNotify(uint code,int item)
  753. {
  754.   HD_NOTIFY notify;
  755.  
  756.   notify.hdr.hwndFrom = GetHandle();
  757.   notify.hdr.idFrom = GetId();
  758.   notify.hdr.code = code;
  759.   notify.iItem = item;
  760.   notify.iButton = 0;
  761.   if(GetKeyState(VK_RBUTTON)<0) {
  762.     notify.iButton |= 1;
  763.   }
  764.   if(GetKeyState(VK_MBUTTON)<0) {
  765.     notify.iButton |= 2;
  766.   }
  767.   notify.pitem = &Item[item];
  768.   return Parent->SendMessage(WM_NOTIFY, GetId(), reinterpret_cast<LPARAM>(¬ify));
  769. }
  770.  
  771. void TColumnHeader::TrackLine(int pos)
  772. {
  773.   // Determine start and end points of tracking lines in screen coordinates
  774.   TRect clientRect=GetClientRect();
  775.  
  776.   TPoint start,end;
  777.  
  778.   if((Attr.Style & HDS_HORZ)==HDS_HORZ) {
  779.     start.x=end.x=pos;
  780.     start.y=clientRect.top;
  781.     end.y=clientRect.bottom;
  782.   }
  783.   else {
  784.     start.y=end.y=pos;
  785.     start.x=clientRect.left;
  786.     end.x=clientRect.right;
  787.   }
  788.  
  789.   // Move track lines in track window
  790.   TClientDC dc(GetHandle());
  791.   dc.SelectStockObject(WHITE_PEN);
  792.   int oldROP2=dc.SetROP2(R2_XORPEN);
  793.   dc.MoveTo(start);
  794.   dc.LineTo(end);
  795.   dc.SetROP2(oldROP2);
  796. }
  797.  
  798. void TColumnHeader::BeginTrack(const TPoint &point,int iItem)
  799. {
  800.   Operation=TRACKING;
  801.   // Set initial tracking parameters
  802.   TrackPoint=point;
  803.   HotItem=iItem;
  804.   TrackSize=Item[HotItem].cxy;
  805.   TrackStart=0;
  806.   for(int index=0;index<HotItem;++index)
  807.     TrackStart+=Item[index].cxy;
  808.   TrackLine(TrackStart+TrackSize);
  809. }
  810.  
  811.  
  812. void TColumnHeader::Track(const TPoint &point)
  813. {
  814.   // erase line at previous position
  815.   TrackLine(TrackStart+TrackSize);
  816.   if((Attr.Style & HDS_HORZ)==HDS_HORZ)
  817.     TrackSize=Item[HotItem].cxy+point.x-TrackPoint.x;
  818.   else
  819.     TrackSize=Item[HotItem].cxy+point.y-TrackPoint.y;
  820.   if(TrackSize<0)
  821.     TrackSize=0;
  822.   // draw line at new position
  823.   TrackLine(TrackStart+TrackSize);
  824. }
  825.  
  826. void TColumnHeader::EndTrack()
  827. {
  828.   Operation=NONE;
  829.   TrackLine(TrackStart+TrackSize);
  830.   Item[HotItem].cxy=TrackSize;
  831.   Invalidate();
  832.   SendNotify(HDN_ENDTRACK,HotItem);
  833. }
  834.  
  835. void TColumnHeader::SetNativeUse(TNativeUse nu)
  836. {
  837.   ClassNativeUse=nu;
  838. }
  839.  
  840. void TColumnHeader::Paint(TDC &dc,bool /*erase*/,TRect &rect)
  841. {
  842.   // all text will be drawn like buttons (even if they're not)
  843.   dc.SetTextColor(TColor::SysBtnText);
  844.   dc.SetBkColor(TColor::Sys3dFace);
  845.   dc.SetBkMode(TRANSPARENT);
  846.  
  847.   // Set initial output box
  848.   TRect clientRect=GetClientRect();
  849.  
  850.   if(Item.IsEmpty()) {
  851.     dc.TextRect(clientRect);
  852.       return;
  853.   }
  854.  
  855.   TPoint topLeft=clientRect.TopLeft();
  856.  
  857.   if(Font!=NULL)
  858.     dc.SelectObject(*Font);
  859.  
  860.   // compute the size of the horizontal and vertical spacing we may need (to
  861.   // prevent the headings to "stick" to close to the edge).
  862.   TEXTMETRIC tm;
  863.   dc.GetTextMetrics(tm);
  864.   TSize spaceSize(tm.tmAveCharWidth,tm.tmHeight);
  865.  
  866.   // Draw everything
  867.   for(uint index=0;index<Item.GetItemsInContainer();++index) {
  868.  
  869.     // compute the item rectangle for this item
  870.     TSize size;
  871.     if((Attr.Style & HDS_HORZ)==HDS_HORZ)
  872.       size=TSize(Item[index].cxy,clientRect.Height());
  873.     else
  874.       size=TSize(clientRect.Width(),Item[index].cxy);
  875.  
  876.     TRect itemRect(topLeft,size);
  877.  
  878.     // Only paint if item is visible and in clipping rectangle
  879.     if(Item[index].cxy>0 && itemRect.Touches(rect)) {
  880.       // draw the line separating 2 items
  881.       if((Attr.Style & HDS_HORZ)==HDS_HORZ) {
  882.         --itemRect.right;
  883.         dc.MoveTo(itemRect.TopRight());
  884.         dc.LineTo(itemRect.BottomRight());
  885.       }
  886.       else {
  887.         --itemRect.bottom;
  888.         dc.MoveTo(itemRect.BottomLeft());
  889.         dc.LineTo(itemRect.BottomRight());
  890.       }
  891.  
  892.       TUIBorder::TStyle style;
  893.       if(Attr.Style & HDS_BUTTONS)
  894.           style=(Pressed==(int)index)?TUIBorder::ButtonDn:TUIBorder::ButtonUp;
  895.       else
  896.         style=TUIBorder::Plain;
  897.  
  898.       TUIBorder border(itemRect,style);
  899.       border.Paint(dc);
  900.       itemRect=border.GetClientRect();
  901.  
  902.       if(Item[index].fmt & HDF_OWNERDRAW) {
  903.         // if the item is owner drawn, send a WM_DRAWITEM to paint it
  904.         DRAWITEMSTRUCT drawStruct;
  905.  
  906.         // we can't write the following, since ODT_HEADER is not defined
  907.         drawStruct.CtlType=ODT_HEADER;
  908.         drawStruct.CtlID=Attr.Id;
  909.         drawStruct.itemID=index;
  910.         drawStruct.itemAction=ODA_DRAWENTIRE;
  911.         drawStruct.itemState=(Pressed==(int)index)?ODS_SELECTED:0;
  912.         drawStruct.hwndItem=*this;
  913.         drawStruct.hDC=dc;
  914.         drawStruct.rcItem=itemRect;
  915.         drawStruct.itemData=Item[index].lParam;
  916.         Parent->SendMessage(WM_DRAWITEM,Attr.Id,reinterpret_cast<LPARAM>(&drawStruct));
  917.       }
  918.       else {
  919.         // the item is either a text, a bitmap or both.
  920.         // erase the background
  921.         dc.TextRect(itemRect);
  922.  
  923.         // get the size of the (optional) bitmap
  924.         TSize bitmapSize;
  925.  
  926.         if(Item[index].fmt & HDF_BITMAP) {
  927.           TBitmap bitmap(Item[index].hbm);
  928.           bitmapSize.cx=bitmap.Width();
  929.           bitmapSize.cy=bitmap.Height();
  930.           }
  931.         else
  932.           bitmapSize.cx=bitmapSize.cy=0;
  933.  
  934.         // get the size of the (optional) string
  935.         TSize stringSize;
  936.  
  937.         if(Item[index].fmt & HDF_STRING)
  938.           stringSize=dc.GetTextExtent(Item[index].pszText,Item[index].cchTextMax);
  939.         else
  940.           stringSize.cx=stringSize.cy=0;
  941.  
  942.         // compute the size of the entire item
  943.         TSize itemSize;
  944.  
  945.         if((Attr.Style & HDS_HORZ)==HDS_HORZ) {
  946.           itemSize.cy=max(bitmapSize.cy,stringSize.cy);
  947.           itemSize.cx=bitmapSize.cx+stringSize.cx;
  948.           // add space between bitmap and text if bitmap is present
  949.           if(Item[index].fmt & HDF_BITMAP)
  950.             itemSize.cx+=spaceSize.cx;
  951.         }
  952.         else {
  953.           itemSize.cx=max(bitmapSize.cx,stringSize.cx);
  954.           itemSize.cy=bitmapSize.cy+stringSize.cy;
  955.           // add space between bitmap and text if bitmap is present
  956.           if(Item[index].fmt & HDF_BITMAP)
  957.             itemSize.cy+=spaceSize.cy;
  958.         }
  959.  
  960.         // with the information we now have, we can align the item as a whole
  961.         // 'p' will be the point where we'll begin drawing the item
  962.         TPoint p=itemRect.TopLeft();
  963.         if((Attr.Style & HDS_HORZ)==HDS_HORZ) {
  964.           if(Item[index].fmt & HDF_LEFT)
  965.             p.x+=spaceSize.cx;
  966.           else if(Item[index].fmt & HDF_CENTER)
  967.             p.x+=(itemRect.Width()-itemSize.cx)/2;
  968.           else if(Item[index].fmt & HDF_RIGHT)
  969.             p.x=itemRect.right-(spaceSize.cx+itemSize.cx);
  970.         }
  971.         else {
  972.           if(Item[index].fmt & HDF_LEFT)
  973.             p.y+=spaceSize.cy;
  974.           else if(Item[index].fmt & HDF_CENTER)
  975.             p.y+=(itemRect.Height()-itemSize.cy)/2;
  976.           else if(Item[index].fmt & HDF_RIGHT)
  977.             p.y=itemRect.bottom-(spaceSize.cy+itemSize.cy);
  978.         }
  979.  
  980.         // paint the (optional) bitmap
  981.         if(Item[index].fmt & HDF_BITMAP) {
  982.           TBitmap bitmap(Item[index].hbm);
  983.           TMemoryDC memdc;
  984.           memdc.SelectObject(bitmap);
  985.           dc.BitBlt(TRect(p,bitmapSize),memdc,TPoint(0,0));
  986.           p.x+=bitmapSize.cx+spaceSize.cx;
  987.         }
  988.         // paint the (optional) string
  989.         if(Item[index].fmt & HDF_STRING)
  990.           dc.DrawText(Item[index].pszText,Item[index].cchTextMax,TRect(p,stringSize),DT_NOPREFIX|DT_NOCLIP);
  991.         }
  992.       }
  993.  
  994.       // Set for next part
  995.       if((Attr.Style & HDS_HORZ)==HDS_HORZ)
  996.         topLeft.x+=size.cx;
  997.       else
  998.         topLeft.y+=size.cy;
  999.     }
  1000.  
  1001.   // Erase last part
  1002.   TRect drawBox=clientRect;
  1003.   drawBox.left=topLeft.x;
  1004.   drawBox.top=topLeft.y;
  1005.   if((Attr.Style & HDS_HORZ)==HDS_HORZ)
  1006.     drawBox.right=clientRect.right;
  1007.   else
  1008.     drawBox.bottom=clientRect.bottom;
  1009.  
  1010.   if(!drawBox.IsEmpty() && drawBox.Touches(rect))
  1011.     dc.TextRect(drawBox);
  1012.  
  1013.   // Restore context
  1014.   dc.RestoreObjects();
  1015. }
  1016.  
  1017. void TColumnHeader::EvSetFont(HFONT hFont, bool redraw)
  1018. {
  1019.   TControl::EvSetFont(hFont, redraw);
  1020.   if(NativeUse & nuUsing) {
  1021.     return;
  1022.   }
  1023.  
  1024.   // Create new font. If header has any items, redraw.
  1025.   delete Font;
  1026.   Font = new TFont(hFont);
  1027.   if (redraw && GetCount() > 0) {
  1028.     Invalidate(true);
  1029.   }
  1030. }
  1031.  
  1032. HFONT TColumnHeader::EvGetFont()
  1033. {
  1034.   if(NativeUse & nuUsing)
  1035.     return TControl::EvGetFont();
  1036.   else {
  1037.     if(Font)
  1038.       return *Font;
  1039.     else
  1040.       return (HFONT)DefaultProcessing();
  1041.   }
  1042. }
  1043.  
  1044. bool TColumnHeader::EvSetCursor(HWND hWndCursor,uint hitTest,uint mouseMsg)
  1045. {
  1046.   if(NativeUse & nuUsing)
  1047.     return TControl::EvSetCursor(hWndCursor,hitTest,mouseMsg);
  1048.  
  1049.   if(hitTest==HTCLIENT) {
  1050.     TPoint p;
  1051.  
  1052.     GetCursorPos(p);
  1053.     ScreenToClient(p);
  1054.     THeaderHitTestInfo ht(p);
  1055.     HitTest(ht);
  1056.     if(ht.flags & HHT_ONDIVOPEN)
  1057.       SetCursor(::Module,((Attr.Style & HDS_HORZ)==HDS_HORZ)?IDC_SPLITH:IDC_SPLITV);
  1058.     else if(ht.flags & HHT_ONDIVIDER)
  1059.       SetCursor(::Module,((Attr.Style & HDS_HORZ)==HDS_HORZ)?IDC_SIZEBARH:IDC_SIZEBARV);
  1060.     else
  1061.       SetCursor(0,IDC_ARROW);
  1062.     }
  1063.  
  1064.   // Now call base class to change cursor
  1065.   return TControl::EvSetCursor(hWndCursor,hitTest,mouseMsg);
  1066. }
  1067.  
  1068. void TColumnHeader::EvLButtonDown(uint modKeys,TPoint &point)
  1069. {
  1070.   // Invoke defautl processing
  1071.   if(NativeUse & nuUsing)
  1072.     TControl::EvLButtonDown(modKeys,point);
  1073.   else {
  1074.     // See if we are on an item start tracking if so
  1075.     THeaderHitTestInfo ht(point);
  1076.     if(HitTest(ht)!=-1) {
  1077.       if((ht.flags & HHT_ONHEADER) && (Attr.Style & HDS_BUTTONS)) {
  1078.         HotItem=Pressed=ht.iItem;
  1079.         Invalidate(false);
  1080.         Operation=CLICKING;
  1081.         SetCapture();
  1082.       }
  1083.       else if(ht.flags & (HHT_ONDIVIDER|HHT_ONDIVOPEN)) {
  1084.         if(SendNotify(HDN_BEGINTRACK,ht.iItem))
  1085.           return;
  1086.         BeginTrack(point,ht.iItem);
  1087.         SetCapture();
  1088.       }
  1089.     }
  1090.   }
  1091. }
  1092.  
  1093. void TColumnHeader::EvLButtonDblClk(uint modKeys,TPoint &point)
  1094. {
  1095.   if(NativeUse & nuUsing)
  1096.     TControl::EvLButtonDblClk(modKeys, point);
  1097.   else {
  1098.     THeaderHitTestInfo ht(point);
  1099.     if(HitTest(ht)!=-1) {
  1100.       if((ht.flags & HHT_ONHEADER) && (Attr.Style & HDS_BUTTONS))
  1101.         SendNotify(HDN_ITEMDBLCLICK,ht.iItem);
  1102.       else if(ht.flags & (HHT_ONDIVIDER|HHT_ONDIVOPEN))
  1103.         SendNotify(HDN_DIVIDERDBLCLICK,ht.iItem);
  1104.     }
  1105.   }
  1106. }
  1107.  
  1108. void TColumnHeader::EvMouseMove(uint modKeys,TPoint &point)
  1109. {
  1110.   if(NativeUse & nuUsing)
  1111.     TControl::EvMouseMove(modKeys,point);
  1112.   else
  1113.     switch(Operation) {
  1114.       case CLICKING:
  1115.       {
  1116.         THeaderHitTestInfo ht(point);
  1117.         if(HitTest(ht)==HotItem) {
  1118.           // mouse in the header initially clicked on
  1119.           if(Pressed!=HotItem) {
  1120.             Pressed=HotItem;
  1121.             Invalidate(false);
  1122.           }
  1123.         }
  1124.         else {
  1125.           // mouse left the header initially clicked on
  1126.           if(Pressed!=-1) {
  1127.             Pressed=-1;
  1128.             Invalidate(false);
  1129.           }
  1130.         }
  1131.       }
  1132.       break;
  1133.  
  1134.       case TRACKING:
  1135.       {
  1136.         if(SendNotify(HDN_TRACK,HotItem)) {
  1137.           EndTrack();
  1138.           ReleaseCapture();
  1139.         }
  1140.         else
  1141.           Track(point);
  1142.       }
  1143.       break;
  1144.     } // switch
  1145. }
  1146.  
  1147. void TColumnHeader::EvLButtonUp(uint modKeys,TPoint &point)
  1148. {
  1149.   if(NativeUse & nuUsing)
  1150.     TControl::EvLButtonUp(modKeys,point);
  1151.   else
  1152.     switch(Operation) {
  1153.       case CLICKING:
  1154.         // If this seems a little strange, note that Windows95 sends a
  1155.         // HDN_ITEMCLICK on WM_LBUTTONUP, so we have to emulate that behaviour.
  1156.         if(Pressed==HotItem) {
  1157.           SendNotify(HDN_ITEMCLICK,HotItem);
  1158.           Pressed=-1;
  1159.           Invalidate(false);
  1160.         }
  1161.         Operation=NONE;
  1162.         ReleaseCapture();
  1163.         break;
  1164.  
  1165.       case TRACKING:
  1166.         EndTrack();
  1167.         ReleaseCapture();
  1168.         break;
  1169.       }
  1170. }
  1171.  
  1172. #endif
  1173.